package com.future.service.Impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.future.allUtils.utils.JwtUtil;
import com.future.allUtils.utils.RegexPatterns;
import com.future.domain.DTO.PasswordDTO;
import com.future.domain.DTO.UserLoginDTO;
import com.future.domain.LoginUserDetails;
import com.future.domain.Result;
import com.future.domain.User;
import com.future.mapper.UserMapper;
import com.future.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

import static com.future.allUtils.constants.RedisCacheConstants.*;
import static com.future.allUtils.utils.RegexPatterns.EMAIL_REGEX;

/**
 * 登录服务实现类
 * @author huayue
 *
 * 1.AuthenticationManager.authenticate(...)进行认证
 * 参数类型是Authentication,因此创建了其实现类对象UsernamePasswordAuthenticationToken
 * 返回值是authentication，封装了认证信息
 * 2.authenticate为空则登录失败
 *           不为空-->其内含有LoginUserDetails对象，封装了用户信息
 * 3.获取用户信息存入redis
 * 4.生成token返回给前端
 * 登陆成功
 */
@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private ObjectMapper objectMapper;  // 使用Jackson的ObjectMapper统一序列化

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    public Result accountLogin(UserLoginDTO user) {
        // 使用账号进行认证
        return authenticateAndGenerateToken(user.getAccount(), user.getPassword(), false);
    }

    @Override
    public Result emailLogin(UserLoginDTO user) {
        // 使用邮箱进行认证
        return authenticateAndGenerateToken(user.getEmail(), user.getPassword(), true);
    }

    /**
     * 通用认证与令牌生成方法
     * @param username 用户名（账号或邮箱）
     * @param password 密码
     * @param isEmailLogin 是否为邮箱登录
     * @return 登录结果
     */
    private Result authenticateAndGenerateToken(String username, String password, boolean isEmailLogin) {

        // 创建认证令牌
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                username,
                password,
                Collections.emptyList()
        );
        Authentication authenticate;
        try {
            // 执行认证流程
            authenticate = authenticationManager.authenticate(authenticationToken);

        } catch (UsernameNotFoundException e) {
            return Result.fail("账号不存在");
        } catch (BadCredentialsException e) {
            return Result.fail("密码错误");
        } catch (AuthenticationException e) {
            if (isEmailLogin) {
                return Result.fail("邮箱未注册");
            } else {
                return Result.fail("账号不存在");
            }
        }

        // 认证成功，获取用户详情
        LoginUserDetails principal = (LoginUserDetails) authenticate.getPrincipal();
        Long userId = principal.getUser().getId();

        // 生成JWT令牌（有效期30分钟）
        String jwt = JwtUtil.createJWT(String.valueOf(userId), JwtUtil.JWT_TTL);

        // 将令牌存入Redis(半个小时)
        stringRedisTemplate.opsForValue().set(
                LOGIN_TOKEN_PREFIX + userId,
                jwt,
                CACHE_NORMAL_TTL, TimeUnit.SECONDS
        );

        //将LoginUserDetail存入redis
        //存的是对象需要序列化
        try {
            String loginUserJson = objectMapper.writeValueAsString(principal);
            stringRedisTemplate.opsForValue().set(
                    USER_INFO_PREFIX + userId,
                    loginUserJson,
                    CACHE_NORMAL_TTL, TimeUnit.SECONDS
            );
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }


        // 构建返回结果，包含JWT令牌
        HashMap<String, String> resultMap = new HashMap<>();
        resultMap.put("token", jwt);
        return Result.ok(resultMap);
    }

    @Override
    public Result logout() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUserDetails principal = (LoginUserDetails) authentication.getPrincipal();
        Long id = principal.getUser().getId();
        stringRedisTemplate.delete(LOGIN_TOKEN_PREFIX + id);
        stringRedisTemplate.delete(USER_INFO_PREFIX + id);
        return Result.ok("退出成功");
    }

    @Override
    public Result forgetPassword(PasswordDTO dto) {
        //1.校验邮箱格式
        if(!StringUtils.hasText(dto.getEmail())){
            return Result.fail("邮箱不能为空");
        }
        if(!StringUtils.hasText(dto.getCaptcha())){
            return Result.fail("验证码错误");
        }
        if (!dto.getEmail().matches(EMAIL_REGEX)){
            System.out.println(dto.getEmail()+"666");
            return Result.fail("邮箱格式有误");
        }

        String captcha = stringRedisTemplate.opsForValue().get(CAPTCHA_PREFIX + dto.getEmail());
        if(!StringUtils.hasText(captcha)){
            return Result.fail("验证码不存在或已过期");
        }

        if(!captcha.equals(dto.getCaptcha())){
            return Result.fail("验证码错误");
        }
        //2.校验新密码格式
        if (!dto.getNewPassword().matches(RegexPatterns.PASSWORD_REGEX)){
            return Result.fail("密码格式有误,应为8-20位，包含大小写字母、数字和特殊符号（!@#$%^&*）" );
        }

        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail,dto.getEmail());
        User user = userMapper.selectOne(lqw);
        user.setPassword(bCryptPasswordEncoder.encode(dto.getNewPassword()));
        int i = userMapper.updateById(user);
        if(i == 0){
            return Result.fail("修改失败,请重试");
        }

        return Result.ok();
    }
}
